import type { MaySuccess, CancellablePromise } from '@gnlab/utils'
import { cancellable } from '@gnlab/utils'
import { MemSubject } from '@gnlab/utils/rx'

export interface LoadResult<ItemsT> {
  items: ItemsT // 此次加载到的数据
  full: boolean // 是否已加载完所有数据
}

export enum LoadStatus {
  Idle = 'Idle', // 未进行过加载
  Partial = 'Partial', // 已加载部分数据
  Loading = 'Loading', // 加载中...
  Full = 'Full', // 已加载所有数据
}

// ==================================================

/**
 * 加载器基类
 */
interface BaseLoaderData<CondT> {
  status: LoadStatus
  conditions: CondT
}

abstract class BaseInfiniteLoader<
  CondT,
  ItemsT,
  LoaderDataT extends BaseLoaderData<CondT> = BaseLoaderData<CondT>,
> {
  protected readonly _data = new MemSubject<LoaderDataT>(this.getInitialData())
  readonly data = this._data.asStream()

  get status() {
    return this.data.value.status
  }
  get conditions() {
    return this.data.value.conditions
  }
  get loadingConditions() {
    return this.loading?.conditions ?? null
  }

  /**
   * 返回初始的查询条件
   */
  protected abstract getInitialData(): LoaderDataT

  /**
   * 基于部分更新过的当前 loaderData 和最新一次的数据加载结果，生成新的 loaderData
   */
  protected abstract formatLoaderData(
    updatedData: LoaderDataT,
    loadResult: LoadResult<ItemsT>,
  ): LoaderDataT

  /**
   * 发起查询
   */
  protected abstract request(conditions?: CondT): {
    conditions: CondT
    promise: Promise<MaySuccess<LoadResult<ItemsT>>>
  }

  protected loading: {
    conditions: CondT
    promise: CancellablePromise<MaySuccess<LoadResult<ItemsT>>>
  } | null = null
  load(conditions?: CondT) {
    if (this.status === LoadStatus.Full) return
    if (this.loading) return this.loading

    this._data.next({ ...this.data.value, status: LoadStatus.Loading })

    const request = this.request(conditions)
    this.loading = {
      promise: cancellable(request.promise),
      conditions: request.conditions,
    }
    void this.loading.promise.then(res => {
      this.loading = null

      if (!res.success) {
        this._data.next({ ...this.data.value, status: LoadStatus.Partial })
      } else {
        const partialNextData = {
          ...this.data.value,
          status: res.data.full ? LoadStatus.Full : LoadStatus.Partial,
          conditions: request.conditions,
        }
        const nextData = this.formatLoaderData(partialNextData, res.data)
        this._data.next(nextData)
      }
    })
    return this.loading
  }

  /**
   * 取消正在进行的加载
   */
  cancel() {
    if (this.loading) {
      this._data.next({ ...this.data.value, status: LoadStatus.Partial })
      this.loading.promise.cancel()
      this.loading = null
    }
  }
}

// ==================================================

/**
 * 不存储加载到的数据的加载器
 */
export type BareLoaderData<CondT> = BaseLoaderData<CondT>

export abstract class BareInfiniteLoader<CondT, ItemsT> extends BaseInfiniteLoader<CondT, ItemsT> {
  protected formatLoaderData(current: BareLoaderData<CondT>) {
    return current
  }
}

// ==================================================

/**
 * 会存储加载到的数据的加载器
 */
export interface LoaderData<CondT, ItemsT> extends BaseLoaderData<CondT> {
  items: ItemsT
}

export abstract class InfiniteLoader<CondT, ItemsT> extends BaseInfiniteLoader<
  CondT,
  ItemsT,
  LoaderData<CondT, ItemsT>
> {
  protected abstract mergeItems(prev: ItemsT, current: ItemsT, currentConditoins: CondT): ItemsT // 合并原数据和新加载到的数据

  protected formatLoaderData(
    updatedData: LoaderData<CondT, ItemsT>,
    loadResult: LoadResult<ItemsT>,
  ) {
    return {
      ...updatedData,
      items: this.mergeItems(updatedData.items, loadResult.items, updatedData.conditions),
    }
  }

  /**
   * 供外界手动更新 items
   */
  updateItems(items: ItemsT) {
    this._data.next({ ...this.data.value, items })
  }
}
